﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Reflection.Emit;
using System.Data.Common;

namespace DS2171Utils
{
  
       public class DynamicBuilder<T>
       {
           private static readonly MethodInfo getValueMethod =
               typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
           private static readonly MethodInfo isDBNullMethod =
               typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
           
           private delegate T Load(IDataRecord dataRecord);
         
           private Load handler;
           
           private DynamicBuilder() { }

           public T BuildSingle(IDataRecord dataRecord)
           {
               return handler(dataRecord);
           }

           public IList<T> BuildAll(System.Data.Common.DbDataReader  dataReader)
           {
               IList<T> listRet = new List<T>();

               while(dataReader.Read())
               {
                   listRet.Add(handler(dataReader));
               }
               return listRet;

           }//fM

           public static DynamicBuilder<T> CreateBuilder(IDataRecord dataRecord)
           {
               //var nullInDb = dataRecord.GetType().GetMethod("IsDBNull");

               DynamicBuilder<T> dynamicBuilder = new DynamicBuilder<T>();

               DynamicMethod method = new DynamicMethod("DynamicCreate", typeof(T),
                       new Type[] { typeof(IDataRecord) }, typeof(T), true);
               ILGenerator generator = method.GetILGenerator();

               LocalBuilder result = generator.DeclareLocal(typeof(T));
               generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
               generator.Emit(OpCodes.Stloc, result);

               for (int i = 0; i < dataRecord.FieldCount; i++)
               {
                   PropertyInfo propertyInfo = typeof(T).GetProperty(dataRecord.GetName(i));

                   try
                   {
                       if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                       {
                           Label endIfLabel = generator.DefineLabel();
                           generator.Emit(OpCodes.Ldarg_0);
                           generator.Emit(OpCodes.Ldc_I4, i);
                           generator.Emit(OpCodes.Callvirt, isDBNullMethod);//isDBNullMethod
                           generator.Emit(OpCodes.Brtrue, endIfLabel);//

                           generator.Emit(OpCodes.Ldloc, result);
                           generator.Emit(OpCodes.Ldarg_0);
                           generator.Emit(OpCodes.Ldc_I4, i);
                           generator.Emit(OpCodes.Callvirt, getValueMethod);
                           generator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                           generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());

                           generator.MarkLabel(endIfLabel);

                       }
                   }
                   catch (Exception ex)
                   {
                       var ms = ex.Message;
                   }

               }

               generator.Emit(OpCodes.Ldloc, result);
               generator.Emit(OpCodes.Ret);

               dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
               return dynamicBuilder;
           }
       }//fC


}
